package org.kth.dks;

import org.kth.dks.dks_comm.DKSRef;
import org.kth.dks.dks_exceptions.DKSIdentifierAlreadyTaken;
import org.kth.dks.dks_exceptions.DKSRefNoResponse;
import org.kth.dks.dks_exceptions.DKSTooManyRestartJoins;
import org.kth.dks.dks_marshal.DKSMessage;
import org.kth.dks.dks_marshal.FailureMsg;

/**
 * DKSInterface represents a basic routing interface
 * which essentially lets DKS nodes join  and leave the system.
 * It also provides a basic operation to find the node responsible
 * for a given identifier
 *
 * Note that keys are represented by long (64 bit) integers in this interface.
 * It is up to other services using this interface to translate application
 * specific keys to this representation.
 */

public interface DKSInterface  {

  /**
   * Adds a new node to the network managed by the local DKS implementation.
   *
   * @param nodeId The unique DKS node identifier (key) for the new node.
   * @param nodeAddress The location of the new node on the physical underlying
network.
   */
  public void join(DKSRef existingnodeAddress )
      throws DKSTooManyRestartJoins, DKSIdentifierAlreadyTaken, DKSRefNoResponse;

  /**
   * Called by the first node in a ring, this will create a new DKS network containing one node only
   * @param existingnodeAddress DKSOverlayAddress
   */
  public void create();

  /**
   * Set the logging level
   * @param level int, 0 means no logging, 1 means debug prints, 2 means XML dumps
   */
  public void logLevel(int level);

    /**
     * Disconnect the local node from the network.
     */
    public void leave();

    /**
     * Will find the DKS node that is responsible for the given identifier
     * @param identifier whose responsible node is being queried for
     * @return DKSRef reference to the responsible node
     */
    public DKSRef findResponsible(long identifier);

    /**
     * Will route a message the DKS node that is responsible for the given identifier, and call
     * routeCallback() at that node and give it the payload, upon which the responsible node will respond
     * with a payload which is returned to the caller
     * @param identifier whose responsible node being routed to
     * @param payload to be sent to the responsible node
     * @return DKSOBject payload sent back by the responsible node
     * @see DKSAppInterface
     */
    public DKSObject route(long identifier, DKSObject payload);

    /**
     * Will route a message the DKS node that is responsible for the given identifier, and call
     * routeCallbackAsync() at that node and give it the payload, upon which the responsible node will respond
     * with a payload which is returned to the caller
     * @param identifier whose responsible node being routed to
     * @param payload to be sent to the responsible node
     * @see DKSAppInterface
     */
    public void routeAsync(long identifier, DKSObject payload);

    /**
     * Will route a message, starting at firstNode, the DKS node that is responsible for the given identifier, and call
     * routeCallbackAsync() at that node and give it the payload, upon which the responsible node will respond
     * with a payload which is returned to the caller.
     * @param identifier long
     * @param payload DKSObject
     * @param firstNode DKSRef
     */
    public void routeAsyncFrom(long identifier, DKSObject payload, DKSRef firstNode);

    /**
     * Broadcasts the given message.
     */
    public void broadcast(DKSObject message);

    /**
     * Broadcasts the given message.
     */
    public void broadcastRestricted(DKSObject message, long startId, long endId);

    /**
     * Returns the DKSURL (stringified DKSRef) used by this DKS node
     * @return String
     * @see org.kth.dks.dks_comm.DKSRef
     */
    public String getDKSURL();

    /**
     * Returns the DKSRef associated to this DKS node
     * @return DKSRef
     * @see org.kth.dks.dks_comm.DKSRef
     */
    public DKSRef getDKSRef();

  /**
   * Every object implementing this interface gets registered in a ConnectionManager, this method
   * this method unregisters the object in the ConnectionManager. This method needs to be called
   * especially if another object of this type is to be instantiated with the same DKSNetAddress (overlay identity)
   */
  public void unregisterNode();

  /**
   * Sends a DKSMessage to a DKS node
   * @param target a DKSRef to a node in the system
   * @param message a DKSMessage 
   */
  public void send(DKSRef target, DKSMessage message);


  /**
   * Registers a handler for a DKSMessage. DKS will invoke the handler whenever a DKSMessage of that 
   * type is received.
   * @param msg Some object which is a subclass of DKSMessage
   * @param handlerObject Object whose method will be invoked upon receipt of a message of type DKSMessage
   * @param methodName full case sensitive of the method to be invoked(without paranthesis). The method has a predefined signature 
   * as described in the following example. 
   * Example: if the method name is "handleStore", there should a method in handlerObject with the signature
   * <br><code>public void handleStore(DKSRef sender, msgClass msg)</code><br>, where msgClass is the tye of the parameter <code>msg</code>. 
   * @return true if registration succeeded, or false if it failed (which can occur if a DKSMessage with the same internal string exists).
   */
  public boolean addMsgHandler(DKSMessage msg, Object handlerObject, String methodName);

}
